/*global echo pack trim ActiveXObject CreateObject sleep Enumerator addMenuItem echo inputText sleep
pspadVersion getVarValue moduleExists moduleVersion moduleFileName modulePath closeEditorByTitle
closeEditorByIndex closeAllEditors newEditor editorsCount setClipboardText getClipboardText
runPSPadAction logClear logAddLine logGetLine logSetLine logLinesCount projectSave projectFilesCount
projectFiles projectFileName ftpConnect ftpDisconnect ftpCommand ftpDownloadFile ftpUploadFile
*/

/*jshint strict:false wsh:true unused:false
*/


var menuName = "Wim-II",
    module_name = "Wim-II JScript",
    module_ver = "1.1.1",
    globalPattern = "",
    kStrCrlf = "\r\n",
    handyMenuName = "Wim-II",
    quoteCharacter = "'";

/*
2262:
Script function getVarValue() returns any system variable
Modified script function inputText(label, default, cancel: string): string. If
filled in optional "cancel value", dialog returns it if Cancel button was
pressed.
*/

function Init()
{
  /*jshint white:false*/
  addMenuItem("Trim Right"              , menuName, "menutrimRightClick", "Shift+Ctrl+Alt+T");
  addMenuItem("Single Line DivaWeb Log" , menuName, "singleLineClick", "");
  addMenuItem("Single Line DivaWeb Log Selected" , menuName, "singleLineSelectedClick", "");
  addMenuItem("editor test"             , menuName, "editorTestClick", "");
  addMenuItem("Reformat \\r, \\n and \\t"  , menuName, "convertToNewLineClick", "Shift+Ctrl+Alt+R");
  addMenuItem("Trim in quotes"          , menuName, "trimInQuotes", "");
  addMenuItem("-"                       , menuName, "");
  addMenuItem("linear-gradient"         , menuName, "linearGradient", "");
  addMenuItem("-"                       , menuName, "");
  addMenuItem("stack to seqeunce BCB"   , menuName, "stackToSequenceBCB", "");
  addMenuItem("stack to seqeunce Qt"    , menuName, "stackToSequenceQt", "Shift+Ctrl+F3");
  addMenuItem("stack to seqeunce Qt with Qt"    , menuName, "stackToSequenceQtQt", "Shift+Ctrl+Alt+F3");
  addMenuItem("code to sequence"        , menuName, "codeToSequence", "");
  addMenuItem("-"                       , menuName, "");
  addMenuItem("new virtual host"        , menuName, "newVirtualHost");
  addMenuItem("-"                       , menuName, "");
  addMenuItem("Edit Script File"        , menuName, "menuOpenScript"                        );
  addMenuItem("-"                       , menuName, "");
  addMenuItem("testStackSplit"          , menuName, "testStackSplit");
}

function menuOpenScript()
{
  var editor = newEditor();
  editor.openFile(moduleFileName(module_name));
}

function getActiveEditor()
{
  var editor = newEditor();
  editor.assignActiveEditor();
  return editor;
}


//function ICommon(that){if(that!=null){that.inherit=Common.prototype.inherit;that.specialize=Common.prototype.specialize}return that};ICommon.specialize=function(p,c){if(!p)p={};if(!c)c=p.constructor;if(c=={}.constructor)c=new Function("this.inherit()");c.valueOf=new Function("return this");c.valueOf.prototype=new this.valueOf;c.valueOf.prototype.specialize(p);c.prototype=new c.valueOf;c.valueOf.prototype.constructor=c.prototype.constructor=c;c.ancestor=this;c.specialize=arguments.callee;c.ancestorOf=this.ancestorOf;return c};ICommon.valueOf=new Function("return this");ICommon.valueOf.prototype={constructor:ICommon,inherit:function(){return arguments.callee.caller.ancestor.apply(this,arguments)},specialize:function(that){if(this==this.constructor.prototype&&this.constructor.specialize){return this.constructor.valueOf.prototype.specialize(that)}for(var i in that){switch(i){case"constructor":case"toString":case"valueOf":continue}if(typeof that[i]=="function"&&that[i]!=this[i]){that[i].ancestor=this[i]}this[i]=that[i]}if(that.toString!=this.toString&&that.toString!={}.toString){that.toString.ancestor=this.toString;this.toString=that.toString}return this}};function Common(){};this.Common=ICommon.specialize({constructor:Common,toString:function(){return"[common "+(this.constructor.className||"Object")+"]"},instanceOf:function(klass){return this.constructor==klass||klass.ancestorOf(this.constructor)}});Common.className="Common";Common.ancestor=null;Common.ancestorOf=function(klass){while(klass&&klass.ancestor!=this)klass=klass.ancestor;return Boolean(klass)};Common.valueOf.ancestor=ICommon;function ParseMaster(){var E=0,R=1,L=2;var G=/\(/g,S=/\$\d/,I=/^\$\d+$/,T=/(['"])\1\+(.*)\+\1\1$/,ES=/\\./g,Q=/'/,DE=/\x01[^\x01]*\x01/g;var self=this;this.add=function(e,r){if(!r)r="";var l=(_14(String(e)).match(G)||"").length+1;if(S.test(r)){if(I.test(r)){r=parseInt(r.slice(1))-1}else{var i=l;var q=Q.test(_14(r))?'"':"'";while(i)r=r.split("$"+i--).join(q+"+a[o+"+i+"]+"+q);r=new Function("a,o","return"+q+r.replace(T,"$1")+q)}}_31(e||"/^$/",r,l)};this.exec=function(s){_3.length=0;return _28(_5(s,this.escapeChar).replace(new RegExp(_1,this.ignoreCase?"gi":"g"),_29),this.escapeChar).replace(DE,"")};this.reset=function(){_1.length=0};var _3=[];var _1=[];var _30=function(){return"("+String(this[E]).slice(1,-1)+")"};_1.toString=function(){return this.join("|")};function _31(){arguments.toString=_30;_1[_1.length]=arguments}function _29(){if(!arguments[0])return"";var i=1,j=0,p;while(p=_1[j++]){if(arguments[i]){var r=p[R];switch(typeof r){case"function":return r(arguments,i);case"number":return arguments[r+i]}var d=(arguments[i].indexOf(self.escapeChar)==-1)?"":"\x01"+arguments[i]+"\x01";return d+r}else i+=p[L]}};function _5(s,e){return e?s.replace(new RegExp("\\"+e+"(.)","g"),function(m,c){_3[_3.length]=c;return e}):s};function _28(s,e){var i=0;return e?s.replace(new RegExp("\\"+e,"g"),function(){return e+(_3[i++]||"")}):s};function _14(s){return s.replace(ES,"")}};ParseMaster.prototype={constructor:ParseMaster,ignoreCase:false,escapeChar:""};function pack(_7,_0,_2,_8){var I="$1";_7+="\n";_0=Math.min(parseInt(_0),95);function _15(s){var i,p;for(i=0;(p=_6[i]);i++){s=p(s)}return s};var _25=function(p,a,c,k,e,d){while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p};var _26=function(){if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1}};var _6=[];function _4(p){_6[_6.length]=p};function _18(s){var p=new ParseMaster;p.escapeChar="\\";p.add(/'[^'\n\r]*'/,I);p.add(/"[^"\n\r]*"/,I);p.add(/\/\/[^\n\r]*[\n\r]/," ");p.add(/\/\*[^*]*\*+([^\/][^*]*\*+)*\//," ");p.add(/\s+(\/[^\/\n\r\*][^\/\n\r]*\/g?i?)/,"$2");p.add(/[^\w\x24\/'"*)\?:]\/[^\/\n\r\*][^\/\n\r]*\/g?i?/,I);if(_8)p.add(/;;;[^\n\r]+[\n\r]/);p.add(/\(;;\)/,I);p.add(/;+\s*([};])/,"$2");s=p.exec(s);p.add(/(\b|\x24)\s+(\b|\x24)/,"$2 $3");p.add(/([+\-])\s+([+\-])/,"$2 $3");p.add(/\s+/,"");return p.exec(s)};function _17(s){var p=new ParseMaster;p.add(/((\x24+)([a-zA-Z_]+))(\d*)/,function(m,o){var l=m[o+2].length;var s=l-Math.max(l-m[o+3].length,0);return m[o+1].substr(s,l)+m[o+4]});var r=/\b_[A-Za-z\d]\w*/;var k=_13(s,_9(r),_21);var e=k.e;p.add(r,function(m,o){return e[m[o]]});return p.exec(s)};function _16(s){if(_0>62)s=_20(s);var p=new ParseMaster;var e=_12(_0);var r=(_0>62)?/\w\w+/ :/\w+/;k=_13(s,_9(r),e);var e=k.e;p.add(r,function(m,o){return e[m[o]]});return s&&_27(p.exec(s),k)};function _13(s,r,e){var a=s.match(r);var so=[];var en={};var pr={};if(a){var u=[];var p={};var v={};var c={};var i=a.length,j=0,w;do{w="$"+a[--i];if(!c[w]){c[w]=0;u[j]=w;p["$"+(v[j]=e(j))]=j++}c[w]++}while(i);i=u.length;do{w=u[--i];if(p[w]!=null){so[p[w]]=w.slice(1);pr[p[w]]=true;c[w]=0}}while(i);u.sort(function(m1,m2){return c[m2]-c[m1]});j=0;do{if(so[i]==null)so[i]=u[j++].slice(1);en[so[i]]=v[i]}while(++i<u.length)}return{s:so,e:en,p:pr}};function _27(p,k){var E=_10("e\\(c\\)","g");p="'"+_5(p)+"'";var a=Math.min(k.s.length,_0)||1;var c=k.s.length;for(var i in k.p)k.s[i]="";k="'"+k.s.join("|")+"'.split('|')";var e=_0>62?_11:_12(a);e=String(e).replace(/_0/g,"a").replace(/arguments\.callee/g,"e");var i="c"+(a>10?".toString(a)":"");if(_2){var d=_19(_26);if(_0>62)d=d.replace(/\\\\w/g,"[\\xa1-\\xff]");else if(a<36)d=d.replace(E,i);if(!c)d=d.replace(_10("(c)\\s*=\\s*1"),"$1=0")}var u=String(_25);if(_2){u=u.replace(/\{/,"{"+d+";")}u=u.replace(/"/g,"'");if(_0>62){u=u.replace(/'\\\\b'\s*\+|\+\s*'\\\\b'/g,"")}if(a>36||_0>62||_2){u=u.replace(/\{/,"{e="+e+";")}else{u=u.replace(E,i)}u=pack(u,0,false,true);var p=[p,a,c,k];if(_2){p=p.concat(0,"{}")}return"eval("+u+"("+p+"))\n"};function _12(a){return a>10?a>36?a>62?_11:_22:_23:_24};var _24=function(c){return c};var _23=function(c){return c.toString(36)};var _22=function(c){return(c<_0?'':arguments.callee(parseInt(c/_0)))+((c=c%_0)>35?String.fromCharCode(c+29):c.toString(36))};var _11=function(c){return(c<_0?'':arguments.callee(c/_0))+String.fromCharCode(c%_0+161)};var _21=function(c){return"_"+c};function _5(s){return s.replace(/([\\'])/g,"\\$1")};function _20(s){return s.replace(/[\xa1-\xff]/g,function(m){return"\\x"+m.charCodeAt(0).toString(16)})};function _10(s,f){return new RegExp(s.replace(/\$/g,"\\$"),f)};function _19(f){with(String(f))return slice(indexOf("{")+1,lastIndexOf("}"))};function _9(r){return new RegExp(String(r).slice(1,-1),"g")};_4(_18);if(_8)_4(_17);if(_0)_4(_16);return _15(_7)};

/*==================================================================================================
  Description   String prototype extensions

  20080620      Wim       Created
==================================================================================================*/
String.prototype.leftPad = function (size, ch)
{
  var result = this;
  ch = ch || " ";
  while (result.length < size)
  {
    result = ch + result;
  }
  return result;
};

String.prototype.extractFileName = function ()
{
  var results = this.split('\\');
  return results[results.length - 1];
};

String.prototype.extractFilePath = function ()
{
  var results = this.split('\\');
  return results.slice(0, -1).join('\\');
};

String.prototype.trimRight = function ()
{
  return this.replace(/\s+$/, '');
};

String.prototype.trim = function ()
{
  var result = this.replace(/^\s+/, '');
  result = result.replace(/\s+$/, '');
  return result;
};

String.prototype.lastChar = function ()
{
  return this.charAt(this.length - 1);
};

String.prototype.toInt = function (base)
{
  return parseInt(this, base || 10);
};

String.prototype.find = function (regex)
{
  var result = this.match(regex);
  result = result.length > 0 ? result[0] : '';
  return result;
};

String.prototype.format = function ()
{
  var i, a = arguments, result = this;
  for (i = 0; i < a.length; i += 1)
  {
    result = result.replace('{' + (i) + '}', a[i]);
  }
  return result;
};

String.prototype.replaceAt = function (index, character) 
{
  return this.substr(0, index) + character + this.substr(index + character.length);
};



/*=============================================================================================*//**
  Array prototypes
*///================================================================================================
if (!Array.prototype.indexOf)
{
  Array.prototype.indexOf = function (elt /*, from*/)
  {
    var len = this.length, from;

    from = Number(arguments[1]) || 0;
    from = (from < 0) ? Math.ceil(from) : Math.floor(from);
    if (from < 0)
      from += len;

    for (; from < len; from++)
    {
      if (from in this &&
          this[from] === elt)
        return from;
    }
    return -1;
  };
}


/*==================================================================================================
  Description   Date prototype extensions

  20090508      Wim     Created
==================================================================================================*/
Date.prototype.formatString = function ()
{
  return String(this.getFullYear()) +
         String(this.getMonth() + 1).leftPad(2, "0") +
         String(this.getDate()).leftPad(2, "0");
};


/*==================================================================================================
  Description
  
  20110817      Wim       Created
==================================================================================================*/
var Editor = (function ()
{
  "use strict";
  var itself;

  itself = function ()
  {
    this.editor = newEditor();
    this.editor.assignActiveEditor();
    this.X = this.editor.caretX();
    this.Y = this.editor.caretY();
  };

  itself.prototype.getLine = function (y)
  {
    this.editor.caretY(y);
    return this.editor.lineText();
  };

  itself.prototype.setLine = function (y, line)
  {
    this.editor.caretY(y);
    this.editor.command("ecLineStart");
    this.editor.lineText(line);
  };

  itself.prototype.restoreCaret = function ()
  {
    this.editor.setCaretPos(this.X, this.Y);
  };
  
  itself.prototype.getSelectedLines = function ()
  {
    var text = this.editor.selText(),
        lines = text.split('\r\n');
        
    if ((lines.length > 0) && (lines[lines.length - 1] === ''))
      lines.splice(lines.length - 1, 1);
    return lines;
  };
  
  itself.prototype.setSelectedLines = function (lines)
  {
    this.editor.selText(lines.join('\r\n') + '\r\n');
  };

  return itself;
}());


/*==================================================================================================
  Description
  
  20110817      Wim       Created
==================================================================================================*/
function singleLineClick()
{
  logClear();
  
  var ed, line, y; 
 
  try
  { 
    ed = new Editor();
    
    y = ed.editor.caretY() + 1;
    while (y < ed.editor.linesCount())
    {
      line = ed.getLine(y);
      if (line.search('DivaWeb 201') === 0)
      {
        y++;
      }
      else
      {
        ed.editor.caretY(y - 1);
        ed.editor.command('ecLineEnd');  
        ed.editor.command('ecTab');
        ed.editor.command('ecDeleteChar');
      }
    }
  }
  catch (e)
  {
    logAddLine(e.message);
  }
}


function singleLineSelectedClick()
{
  var ed, text, lines, line, i, j, newlines, newline;
  
  logClear();
  try
  {
    ed = new Editor();
    text = ed.editor.selText();
    lines = text.split('\r\n');
    newlines = [];
    newline = '';
    for (i = 0, j = lines.length; i < j; i++)
    {
      line = lines[i];
      logAddLine(line);
      if (line.search('DivaWeb 201') === 0)
      {
        if (newline !== '')
          newlines.push(newline);
        newline = line;
      }
      else
      {
        newline += ' ' + line;
      }
    }
    if (newline !== '')
      newlines.push(newline);
    ed.editor.selText(newlines.join('\r\n'));
  }
  catch (e)
  {
    logAddLine(e.message);
  }  
}
  


function editorTestClick()
{
  logClear();
  
  var ed;
  
  try
  {
    ed = new Editor();
    logAddLine(ed.editor.lineText());    
  }
  catch (e)
  {
    logAddLine(e.message);
  }
}

function convertToNewLineClick()
{
  var ed = getActiveEditor(),
      text = ed.selText();
      
  text = text.replace(/\\\\r\\\\n/g, "\n");
  text = text.replace(/\\\\r/g, "\n");
  text = text.replace(/\\\\n/g, "\n");
  text = text.replace(/\\r\\n/g, "\n");
  text = text.replace(/\\r/g, "\n");
  text = text.replace(/\\n/g, "\n");
  text = text.replace(/\\t/g, "  ");
  text = text.replace(/\\"/g, "\"");
  ed.selText(text);
}

function menutrimRightClick()
{
  return "Bug in pspad causes to use autoindent when replacing the complete line and also leaves spaces at end of line intact.";
//  var ed, i, l, line;
//
//  try
//  {
//    ed = new Editor();
//    for (i = 0, l = ed.editor.linesCount(); i < l; i ++)
//    {
//      line = ed.getLine(i);
//      line = line.trimRight();
//      ed.setLine(i, line);
//    }
//  }
//  catch (e)
//  {
//    logAddLine(e.message);
//  }
//  ed.restoreCaret();
}

function linearGradient()
{
  var ed, line, lines, newline;
  
  function cb(str, A, B)
  {
    var per = B.toInt() / 100;
    return 'color_stop(' + per + ', ' + A + ')';                        
  }
  try
  {
    
    
    ed = new Editor();
    line = ed.editor.lineText();
    lines = [];

    try
    {
      line = line.replace(/\s*-moz-linear-gradient/, ' -moz-linear-gradient');
      lines.push(line.replace('-moz-', '        ').trim());
      lines.push(line.replace('-moz-', '     -o-'));
      lines.push(line.replace('-moz-', '    -ms-'));
      lines.push(line.replace('-moz-', '   -moz-'));
      lines.push(line.replace('-moz-', '-webkit-'));
      newline = line.replace('-moz-linear-gradient(', '-webkit-gradient(linear, ');
      newline = newline.replace('top', '@#$1');
      newline = newline.replace('bottom', '@#$2');
      newline = newline.replace('@#$1', 'left top, left bottom');
      newline = newline.replace('@#$2', 'left bottom, left top');
      newline = newline.replace(/(#\w+)\s([0-9]*)%/g, cb);
      lines.push(newline);
    }
    catch (e1)
    {
      logAddLine('push: ' + e1.message);
    }
    try
    {
      ed.editor.lineText(lines.join('\n'));
    }
    catch (e2)
    {
      logAddLine('join: ' + e2.message);
    }
  }
  catch (e3)
  {
    logAddLine(e3.message);
  }
}



function newVirtualHost()
{
  var ed, port, lines; 
  
  ed = new Editor();
  port = ed.editor.selText();
  lines = [];
  lines.push("NameVirtualHost *:" + port);
  lines.push("Listen " + port);
  lines.push("<VirtualHost *:" + port + ">");
  lines.push("  ServerName wibek");
  lines.push("  DocumentRoot \"<path>\"");
  lines.push("  ServerAdmin webmaster@wimbekker.nl");
  lines.push("");
  lines.push("  <Directory \"<path>\">");
  lines.push("    Options All -Indexes");
  lines.push("    AllowOverride All");
  lines.push("    Order allow,deny");
  lines.push("    Allow from all");
  lines.push("    Require local");
  lines.push("  </Directory>");
  lines.push("</VirtualHost>");
  ed.editor.selText(lines.join("\n")); 
}


/*=============================================================================================*//**
  \brief Trims a string that is in quotes
  
  Does not account for escaped quotes (\")
                       

  const char *kSettingNameChannel                 = "channel                ";
  const char *kSettingNameVideo                   = "video                  ";
  const char *kSettingNameFramerate               = "framerate              ";
  
  becomes:
  
  const char *kSettingNameChannel                 = "channel"                ;
  const char *kSettingNameVideo                   = "video"                  ;
  const char *kSettingNameFramerate               = "framerate"              ;
  
  
  
*///================================================================================================
function trimLineInQuotes(line) 
{
  var begin, end, index;
  
  begin = line.search('"');
  end = 1 + begin + line.substr(1 + begin).search('"');
  if (begin < end) 
  {
    index = end - 1;
    while ((line.charAt(index) === ' ') && (begin < index))
      index--;
    line = line.replaceAt(end, ' ');
    line = line.replaceAt(index + 1, '"');
  }

  return line;
}


function trimInQuotes()
{
  var ed, lines, i, j;
  
  ed = new Editor();
  lines = ed.getSelectedLines();
  
  for (i = 0, j = lines.length; i < j; i++)
  {
    lines[i] = trimLineInQuotes(lines[i]);
  }
  ed.setSelectedLines(lines); 
}







function stackToSequenceBCB()
{
  try
  {
    var ed, lines, line, i, j, prevobject, definition, scope, object, method, parts, definitions, 
        methods, selstart, sellength, pos, lastpos, unidentified, args; 

    logClear();
    
    unidentified = 1;
    definitions = [];
    methods = [];
    prevobject = '';
    ed = new Editor();
    selstart = ed.editor.selStart();
    sellength = ed.editor.selLength();
    lines = ed.getSelectedLines();


    
    for (i = lines.length - 1; i >= 0; i--)
    {
      try
      {
        line = lines[i].substr(9);  //skip address
        if (line !== '')
        {
          //normalize line:
          //00759A50 TMacroThread::AddActionList(this=:0A4D18E0, DivaActionList=:56032750)
          //TMacroThread::AddActionList
          //line = line.find(/\w*::\w*/);
          
          //extract arguments
          parts = line.split('(');
          args = parts.pop();
          line = parts.join('(');
          
          //brute force remove templates
          line = line.replace(/<.*>/, '');
          
          parts = line.split('::');
          if (parts.length > 2)
          {
            method = parts.pop();
            object = parts.pop();
            scope = parts.join('::');
          }
          else if (parts.length > 1)
          {
            method = parts.pop();
            object = parts.pop();
            scope = '.';
          }
          else if (parts.length > 0)
          {
            method = parts.pop();
            object = 'object-' + unidentified;
            scope = '..';
          }
          else
          {
            method = line;
          }
          definition = object;
          
          if (object.length > 1)
          {
            //Remove Leading 'T'      
            //TMacroThread::AddActionList
            //MacroThread::AddActionList
            if (object.match(/T[A-Z]/))
              object = object.substr(1);
              
            if (object.length > 0)
              object = object.substr(0, 1).toLowerCase() + object.substr(1);
          }
          
          //if previous object is empty use new object
          if (prevobject === '') prevobject = object;
          
          //store definition
          definition = object + ':' + definition;
          if (definitions.indexOf(definition) === -1)
            definitions.push(definition);
            
          //store method
          methods.push(prevobject + ':' + object + '. ' + method + '()');
          
          //remember current object
          prevobject = object;                      
        }
      }
      catch (e)
      {
        logAddLine(e.message);
      }
    }
    lines = [];
    for (i = 0, j = definitions.length; i < j; i++)
      lines.push(definitions[i]);
    lines.push("");
    for (i = 0, j = methods.length; i < j; i++)
      lines.push(methods[i]);
    ed.setSelectedLines(lines);
    ed.editor.selStart(selstart);
    ed.editor.selLength((lines.join('\r\n') + '\r\n').length);
    
  }
  catch (e2)
  {
    logAddLine(e2.message);
  }
}


/*==================================================================================================
Split a string by ' ' taking into account nested < > pairs.
a::b::std::_Rb_tree<std::string, std::pair<std::string const, diva::Parameter>, std::_Select1st<std::pair<std::string const, diva::Parameter>>, std::less<std::string>, std::allocator<std::pair<std::string const, diva::Parameter>>>::_M_lower_bound
becomes a list:
a
b
std
_Rb_tree<std::string, std::pair<std::string const, diva::Parameter>, std::_Select1st<std::pair<std::string const, diva::Parameter>>, std::less<std::string>, std::allocator<std::pair<std::string const, diva::Parameter>>>
_M_lower_bound
==================================================================================================*/
function wordSplit(line)
{
  var result = [], word = '', nest = 0, index = 0, c;
  
  while (index < line.length)
  {
    c = line.charAt(index);
    if (((c === ' ') || (c === '\t'))&& (nest === 0))
    {
      result.push(word.trim());
      word = "";
    }
    else
    {
      word += c;
      if (c === '<') ++nest;
      else if (c === '>') 
      {
        --nest;
        if (nest < 0) nest = 0;
      }
    }
    ++index;
  }
  if (word.length > 0)
    result.push(word.trim());
  return result;
}

/*==================================================================================================
Split a string by '::' taking into account nested < > pairs.
a::b::std::_Rb_tree<std::string, std::pair<std::string const, diva::Parameter>, std::_Select1st<std::pair<std::string const, diva::Parameter>>, std::less<std::string>, std::allocator<std::pair<std::string const, diva::Parameter>>>::_M_lower_bound
becomes a list:
a
b
std
_Rb_tree<std::string, std::pair<std::string const, diva::Parameter>, std::_Select1st<std::pair<std::string const, diva::Parameter>>, std::less<std::string>, std::allocator<std::pair<std::string const, diva::Parameter>>>
_M_lower_bound
==================================================================================================*/
function stackSplit(line)
{
  var result = [], word = '', nest = 0, index = 0, c;
  
  while (index < line.length)
  {
    c = line.charAt(index);
    if ((c == ':') && (++index < line.length) && (line.charAt(index) == ':') && (nest === 0))
    {
      result.push(word.trim());
      word = "";
    }
    else
    {
      word += c;
      if (c === '<') ++nest;
      else if (c === '>') 
      {
        --nest;
        if (nest < 0) nest = 0;
      }
    }
    ++index;
  }
  if (word.length > 0)
    result.push(word.trim());
  return result;
}



function testStackSplit()
{
  var ed, lines, line, words, word;
  
  logClear();
  
  function showRes(res)
  {
    var txt = res.length, i;
    txt += ": ";
    for (i = 0; i < res.length; ++i)
      txt += res[i] + "##";
    logAddLine(txt);
  }
  ed = new Editor();
  lines = ed.getSelectedLines();
        
  for (i = 0; i < lines.length; ++i)
  {
    try
    {
      line = lines[i];
      logAddLine("line: " + line);
      showRes(stackSplit(line));
      logAddLine("-------------------------");
      
      words = wordSplit(line); 
      
      if (words.length > 0)
      {
        word = words.length > 1 ? words[1] : words[0];
        logAddLine("word: " + word);
        showRes(stackSplit(word));
      }
    }
    catch(e)
    {
      logAddLine(e.message);
    }
  }

}


/*==================================================================================================
  Change Qt's stack trace text block to SDedit format
  
0	SetupProfilesAdminCtrl::updateCtrlExposureWindow	SetupProfilesAdminCtrl.cpp	378	0x60499f	
1	SetupProfilesAdminCtrl::exposureWindowChanged	SetupProfilesAdminCtrl.cpp	370	0x60498f	
7	TfoProfilesAdmin::setNoMaskVisible	ufoProfilesAdmin.cpp	771	0x72145a	
8	TfoProfilesAdmin::motionActive	ufoProfilesAdmin.cpp	535	0x720007	
9	TfoProfilesAdmin::setDeviceSpecificControlsVisible	ufoProfilesAdmin.cpp	436	0x71fa79	
10	SetupProfilesAdminCtrl::setProcessSettings	SetupProfilesAdminCtrl.cpp	1171	0x607ff1	
11	TCtrlMainAdmin::handleProcessSettingsChanged	CtrlMainAdmin.cpp	1003	0x546c2d	
12	MainEventsMessageHandler::handleProcessSettingsDisplayChanged	MainEventsMessageHandler.cpp	588	0x45271f	
13	MainEventsMessageHandler::mainEventsMessage	MainEventsMessageHandler.cpp	106	0x4515cf	
14	diva::TCtrlMain::mainEventsMessage	CtrlMain.cpp	998	0x539ace	

becomes

ext: [e]
TCtrlMain: "TCtrlMain"
MainEventsMessageHandler: "MainEventsMessageHandler"
TCtrlMainAdmin: "TCtrlMainAdmin"
SetupProfilesAdminCtrl: "SetupProfilesAdminCtrl"
TfoProfilesAdmin: "TfoProfilesAdmin"

ext:TCtrlMain. EventsMessage
TCtrlMain:TCtrlMain. mainEventsMessage
TCtrlMain:MainEventsMessageHandler. mainEventsMessage		
MainEventsMessageHandler:MainEventsMessageHandler. handleProcessSettingsDisplayChanged		
MainEventsMessageHandler:TCtrlMainAdmin. handleProcessSettingsChanged		
TCtrlMainAdmin:SetupProfilesAdminCtrl. setProcessSettings		
SetupProfilesAdminCtrl:TfoProfilesAdmin. setDeviceSpecificControlsVisible		
TfoProfilesAdmin:TfoProfilesAdmin. motionActive		
TfoProfilesAdmin:TfoProfilesAdmin. setNoMaskVisible		
TfoProfilesAdmin:SetupProfilesAdminCtrl. exposureWindowChanged		
SetupProfilesAdminCtrl:SetupProfilesAdminCtrl. updateCtrlExposureWindow		
	
  
==================================================================================================*/
function internalStackToSequence(withqt)
{
  try
  {
    var definitions, methods, caller, ed, selstart, sellength, lines, i, j, line, words, objectmethod, 
        object, method, definition, namespace;  
    

    logClear();
    
    definitions = [];
    methods = [];
    
    definitions.push('ext: [e]');
    caller = 'ext';


    ed = new Editor();
    selstart = ed.editor.selStart();
    sellength = ed.editor.selLength();
    lines = ed.getSelectedLines();
        
    for (i = lines.length - 1; i >= 0; --i)
    {
      try
      {
        line = lines[i];  
        if (line !== '')
        {
          //normalize line:
          //9	TfoProfilesAdmin::setDeviceSpecificControlsVisible	ufoProfilesAdmin.cpp	436	0x71fa79
          //TfoProfilesAdmin::setDeviceSpecificControlsVisible
          //object = TfoProfilesAdmin 
          //method = setDeviceSpecificControlsVisible
          //definition = TfoProfilesAdmin: "TfoProfilesAdmin"
          
          //extract arguments
          //words = line.split(/\s/);
          words = wordSplit(line);
          if (words.length > 1)
          {
            //objectmethod = words[1].split('::');
            objectmethod = stackSplit(words[1]);
            if (objectmethod.length >= 2)
            {
              namespace = objectmethod[0];
              object = objectmethod[objectmethod.length - 2];

              //ignore object types "boost", "std" and objects starting with "Q" (QComboBox etc)
              if ((object != "") && (namespace != "boost") && (namespace != "std") && ((withqt || object.charAt(0) != 'Q')))
              {
                method = objectmethod[objectmethod.length - 1];
                
                //ignore mockup methods starting with "qt_"
                if (method.substr(0, 3) != "qt_")
                {
                          
                  //store definition
                  definition = object + ': "' + object + '"';
                  if (definitions.indexOf(definition) === -1)
                    definitions.push(definition);
                
                  //store method
                  methods.push(caller + ':' + object + '. ' + method);
                  caller = object;
                }
              }
            }
          }
        }
      }
      catch (e)
      {
        logAddLine(e.message);
      }
    }
    lines = [];
    for (i = 0, j = definitions.length; i < j; i++)
      lines.push(definitions[i]);
    lines.push("");
    for (i = 0, j = methods.length; i < j; i++)
      lines.push(methods[i]);
    ed.setSelectedLines(lines);
    ed.editor.selStart(selstart);
    ed.editor.selLength((lines.join('\r\n') + '\r\n').length);
    
  }
  catch (e2)
  {
    logAddLine(e2.message);
  }
}




function stackToSequenceQt()
{
  internalStackToSequence(false);
}

function stackToSequenceQtQt()
{
  internalStackToSequence(true);
}




/*==================================================================================================
  Change code copied manuall to SDedit format
  
TfoProfilesAdmin::on_cxDeviceActive_clicked
SetupProfilesAdminCtrl::deviceActiveChanged
SetupProfilesAdminCtrl::processSettingsChanged
TCtrlMainAdmin::processSettingsChanged
TCtrlMainAdmin::sendProcessSettingsChanged

becomes

ext: [e]
TfoProfilesAdmin: client
SetupProfilesAdminCtrl: client
SetupProfilesAdminCtrl: client
TCtrlMainAdmin: client
TCtrlMainAdmin: client

ext:TfoProfilesAdmin. on_cxDeviceActive_clicked
TfoProfilesAdmin:SetupProfilesAdminCtrl. deviceActiveChanged
SetupProfilesAdminCtrl:SetupProfilesAdminCtrl. processSettingsChanged
SetupProfilesAdminCtrl:TCtrlMainAdmin. processSettingsChanged
TCtrlMainAdmin:TCtrlMainAdmin. sendProcessSettingsChanged
==================================================================================================*/
function codeToSequence()
{
  try
  {
    var definitions, methods, caller, ed, selstart, sellength, lines, i, j, line, words, objectmethod, 
        object, method, definition;  
    
    logClear();
    
    definitions = [];
    methods = [];
    
    definitions.push('ext: [e]');
    caller = 'ext';


    ed = new Editor();
    selstart = ed.editor.selStart();
    sellength = ed.editor.selLength();
    lines = ed.getSelectedLines();
        
    for (i = 0; i < lines.length; ++i)
    {
      try
      {
        line = lines[i];  
        if (line !== '')
        {
          //normalize line:
          //TfoProfilesAdmin::on_cxDeviceActive_clicked
          //object = TfoProfilesAdmin
          //method = on_cxDeviceActive_clicked
          //definition = TfoProfilesAdmin: client  

          objectmethod = line.split('::');
          if (objectmethod.length === 2)
          {
            object = objectmethod[0];
            method = objectmethod[1];
            definition = object + ': client';
            if (definitions.indexOf(definition) === -1)
              definitions.push(definition);
            methods.push(caller + ':' + object + '. ' + method);
            caller = object;
          }
        }
      }
      catch (e)
      {
        logAddLine(e.message);
      }
    }
    lines = [];
    for (i = 0; i < definitions.length; i++)
      lines.push(definitions[i]);
    lines.push("");
    for (i = 0; i < methods.length; i++)
      lines.push(methods[i]);
    ed.setSelectedLines(lines);
    ed.editor.selStart(selstart);
    ed.editor.selLength((lines.join('\r\n') + '\r\n').length);
  }
  catch (e2)
  {
    logAddLine(e2.message);
  }
}









